/**
*tagfolder
*Licensed under LGPL
*http://code.google.com/p/tagfolder
*chuanshenglu@gmail.com
*xrjieyee@gmail.com
*
*/

#include "TFServerTask.h"
#include <TFstd.h>
#include "TFServerBuffer.h"
#include <iostream>
#include "sqlite3.h"
#include <list>
using namespace std;

//
TFServerTaskDispatcher* TFServerTaskDispatcher::instance=NULL;

//
TFServerTaskDispatcher::TFServerTaskDispatcher()
{
	TaskHandlers.clear();
}

//Delete those small objects;
//Fuck, its so inconveniont to not use boost::shared_ptr
TFServerTaskDispatcher::~TFServerTaskDispatcher()
{
	for ( std::map<long, TFServerTaskHandler*>::iterator itr=TaskHandlers.begin() ;
		itr!=TaskHandlers.end() ;
		itr++ )
	{
		if(itr->second!=NULL)
			delete itr->second;
	}
}
//Get a instance of the singleton object;
TFServerTaskDispatcher*  TFServerTaskDispatcher::GetInstance()
{
	if(instance==NULL)
	{
		instance=new TFServerTaskDispatcher();
	}
	return instance;
}

//Dispatch message , calling the handler;
void TFServerTaskDispatcher::Dispatch(TFMessage& msg,TFDuplexChannel & channel)
{
	std::map<long, TFServerTaskHandler*>::iterator itr=TaskHandlers.find(msg.ID);
	if ( itr != TaskHandlers.end( ) )
	{
		if ( itr->second != NULL )
		{
			TFServerTaskHandler & handler=*( itr->second );
			handler ( msg , channel );
		}
	}
}

//Add task handler;
bool TFServerTaskDispatcher::AddHandler(long sig,TFServerTaskHandler* handler)
{
    if( sig < 0 ){
        return false;
    }
    std::pair< std::map<long,TFServerTaskHandler*>::iterator,bool> p = 
        TaskHandlers.insert( std::pair<long ,TFServerTaskHandler*> ( sig , handler ) ) ;
    if( p.second && p.first!=TaskHandlers.end() ){
        return true;
    }else{
        return false;
    }
}

//Remove task handler;
bool TFServerTaskDispatcher::RemoveHandler(long sig)
{
    if( sig < 0 ) {
        return false;
    }
	std::map<long, TFServerTaskHandler*>::iterator itr=TaskHandlers.find(sig);
	if(itr!=TaskHandlers.end())
	{
		TaskHandlers.erase(itr);
                return true;
	}
        return false;
}

TFServerTaskHandler* TFServerTaskDispatcher::GetHandler( long sig ){
    if( sig < 0 ) {
        return false;
    }
    TFServerTaskHandler *ret=NULL;
    std::map<long, TFServerTaskHandler*>::iterator itr=TaskHandlers.find(sig);
    if( itr!=TaskHandlers.end() ){
        ret=itr->second;
    }
    return ret;
}

//=================================================================
//Predefined task handler
//=================================================================
//On Add tag;
static const char *dbfile="./tf.sqlite";
void TFSTaskOnAddTag::operator ( ) (TFMessage &msg,TFDuplexChannel & channel)
{
    std::string tag=channel.GetString();
    sqlite3* dbhandle;
    if(sqlite3_open(dbfile,&dbhandle)!=0)
        return;
    sqlite3_stmt *stmt;
    string sql("INSERT INTO tag (id, name) vlaues( NULL,\"");
    sql+=tag;
    sql+="\")";
    if( SQLITE_OK!=sqlite3_prepare(dbhandle,sql.c_str(),sql.size(),&stmt,NULL))
    {
        sqlite3_close(dbhandle);
        return;
    }
    sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    sqlite3_close(dbhandle);
    cout <<"EXEC SQL:"<<sql<<endl;
}

//On Add file 
void TFSTaskOnAddFile::operator ( ) ( TFMessage &msg, TFDuplexChannel & channel)
{
    std::string file = channel.GetString();
    sqlite3* dbhandle;
    if(sqlite3_open(dbfile,&dbhandle)!=0)
        return;
    sqlite3_stmt *stmt;
    string sql("INSERT INTO file(id, location,0) vlaues( NULL,\"");
    sql+=file;
    sql+="\")";
    if( SQLITE_OK!=sqlite3_prepare(dbhandle,sql.c_str(),sql.size(),&stmt,NULL))
    {
        sqlite3_close(dbhandle);
        return;
    }
    sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    sqlite3_close(dbhandle);
    cout<<"EXEC SQL:"<<sql<<endl;

    //TFStringBuffer& buf=TFBufferManager::GetInstance().GetStringBuffer("FileBuffer");
    //buf.Add(file);
    //std::cout <<"Add File: " <<file <<std::endl;
}


//On link tag to file
void TFSTaskOnLinkTagToFile::operator ( ) ( TFMessage &msg, TFDuplexChannel & channel)
{
    std::string tag = channel.GetString();
    std::string file = channel.GetString();
    sqlite3* dbhandle;
    if(sqlite3_open(dbfile,&dbhandle)!=0)
        return;
    sqlite3_stmt *stmt;
    
//Get corresponding Tag ID
    string sql("SELECT id FROM tag WHERE name=");
    sql+=tag;
    if( SQLITE_OK!=sqlite3_prepare(dbhandle,sql.c_str(),sql.size(),&stmt,NULL))
    {
        sqlite3_close(dbhandle);
        return;
    }
    int idTag=-1,idFile=-1;
    if(SQLITE_DONE!=sqlite3_step(stmt))
        idTag=sqlite3_column_int(stmt,0);
    sqlite3_finalize(stmt);
    cout<<"EXEC SQL:"<<sql<<endl;

//Get corresponding File ID
    sql="SELECT id FROM file WHERE location=";
    sql+=file;
    if( SQLITE_OK!=sqlite3_prepare(dbhandle,sql.c_str(),sql.size(),&stmt,NULL))
    {
        sqlite3_close(dbhandle);
        return;
    }
    if(SQLITE_DONE!=sqlite3_step(stmt))
        idFile=sqlite3_column_int(stmt,0);
    sqlite3_finalize(stmt);
    cout<<"EXEC SQL:"<<sql<<endl;

//If all IDs are OK, link them together;
    if(idTag==-1 || idFile==-1)
        return ;
    sql="INSERT INTO tagfilelink(tid,fid) VALUES (\"";
    char buf[16];
    sprintf(buf, "%d",idTag);
    sql+=buf;
    sql+="\",\"";
    sprintf(buf, "%d", idFile);
    sql+=buf;
    sql+="\")";
    if( SQLITE_OK!=sqlite3_prepare(dbhandle,sql.c_str(),sql.size(),&stmt,NULL))
    {
        sqlite3_close(dbhandle);
        return;
    }
    sqlite3_step(stmt);
    sqlite3_finalize(stmt);
    sqlite3_close(dbhandle);
    cout<<"EXEC SQL:"<<sql<<endl;

    //TFStringBuffer& tbuf=TFBufferManager::GetInstance().GetStringBuffer("TagBuffer");
    //TFStringBuffer& fbuf=TFBufferManager::GetInstance().GetStringBuffer("FileBuffer");
    //TFLinkBuffer& lbuf=TFBufferManager::GetInstance().GetLinkBuffer("TagFileLinkBuffer"); 
    //std::cout<<"Link tag"<<tag<<"To file "<<file<<std::endl;
}

//Get all tags
void TFSTaskOnGetAllTags::operator ( ) ( TFMessage & msg, TFDuplexChannel & channel)
{
    sqlite3* dbhandle;
    if(sqlite3_open(dbfile,&dbhandle)!=0)
        return;
    sqlite3_stmt *stmt;
    string sql("SELECT * FROM tag");
    if( SQLITE_OK!=sqlite3_prepare(dbhandle,sql.c_str(),sql.size(),&stmt,NULL))
    {
        sqlite3_close(dbhandle);
        return;
    }

    TFMessage m;
    m.ID = TFMSG_GETALLTAGS;
    list<string> taglist;
    while(SQLITE_DONE!=sqlite3_step(stmt))
    {
        std::string tag(
            (const char* )(sqlite3_column_text(stmt, 1) ) 
            );
        taglist.push_back(tag);
    }
    m.NextSize = taglist.size();
    channel.Put( &m, sizeof(m) );
    for(list<string>::iterator itr=taglist.begin(); itr!=taglist.end(); itr++)
    {
        channel.Put( (*itr) );
    }

    sqlite3_finalize(stmt);
    sqlite3_close(dbhandle);
    cout <<"EXEC SQL:"<<sql<<endl;
}

//Get all files
void TFSTaskOnGetAllFiles::operator ( ) ( TFMessage &msg, TFDuplexChannel & channel)
{
    sqlite3* dbhandle;
    if(sqlite3_open(dbfile,&dbhandle)!=0)
        return;
    sqlite3_stmt *stmt;
    string sql("SELECT * FROM file");
    if( SQLITE_OK!=sqlite3_prepare(dbhandle,sql.c_str(),sql.size(),&stmt,NULL))
    {
        sqlite3_close(dbhandle);
        return;
    }

    TFMessage m;
    m.ID = TFMSG_GETALLTAGS;
    list<string> taglist;
    while(SQLITE_DONE!=sqlite3_step(stmt))
    {
        std::string tag(
            (const char *)(sqlite3_column_text(stmt, 1) ) 
            );
        taglist.push_back(tag);
    }
    m.NextSize = taglist.size();
    channel.Put( &m, sizeof(m) );
    for(list<string>::iterator itr=taglist.begin(); itr!=taglist.end(); itr++)
    {
        channel.Put( (*itr) );
    }

    sqlite3_finalize(stmt);
    sqlite3_close(dbhandle);
    cout <<"EXEC SQL:"<<sql<<endl;
}

//TFSTaskOnGetTagOfFile
void TFSTaskOnGetTagsOfFile::operator ( ) ( TFMessage &msg, TFDuplexChannel & channel)
{
    sqlite3* dbhandle;
    if(sqlite3_open(dbfile,&dbhandle)!=0)
        return;
    sqlite3_stmt *stmt;
    std::string file=channel.GetString();
    string sql("SELECT tag.name FROM tag,file,tagfilelink WHERE tag.id=tagfilelink.tid AND file.id=tagfilelink.fid AND file.location=\"");
    sql+=file;
    sql+="\"";
    if( SQLITE_OK!=sqlite3_prepare(dbhandle,sql.c_str(),sql.size(),&stmt,NULL))
    {
        sqlite3_close(dbhandle);
        return;
    }

    TFMessage m;
    m.ID = TFMSG_GETTAGSOFFILE;
    list<string> taglist;
    while(SQLITE_DONE!=sqlite3_step(stmt))
    {
        std::string tag(
            (const char *)(sqlite3_column_text(stmt, 1) ) 
            );
        taglist.push_back(tag);
    }
    m.NextSize = taglist.size();
    channel.Put( &m, sizeof(m) );
    for(list<string>::iterator itr=taglist.begin(); itr!=taglist.end(); itr++)
    {
        channel.Put( (*itr) );
    }

    sqlite3_finalize(stmt);
    sqlite3_close(dbhandle);
    cout <<"EXEC SQL:"<<sql<<endl;
}

//TFSTaskOnGetTagOfFile
void TFSTaskOnGetFilesWithTag::operator ( )(TFMessage &msg, TFDuplexChannel &channel)
{
    sqlite3* dbhandle;
    if(sqlite3_open(dbfile,&dbhandle)!=0)
        return;
    sqlite3_stmt *stmt;
    std::string tag=channel.GetString();
    string sql("SELECT tag.name FROM tag,file,tagfilelink WHERE tag.id=tagfilelink.tid AND file.id=tagfilelink.fid AND tag.name=\"");
    sql+=tag;
    sql+="\"";
    if( SQLITE_OK!=sqlite3_prepare(dbhandle,sql.c_str(),sql.size(),&stmt,NULL))
    {
        sqlite3_close(dbhandle);
        return;
    }

    TFMessage m;
    m.ID = TFMSG_GETFILESWITHTAG;
    list<string> filelist;
    while(SQLITE_DONE!=sqlite3_step(stmt))
    {
        std::string file(
            (const char *)(sqlite3_column_text(stmt, 1) ) 
            );
        filelist.push_back(file);
    }
    m.NextSize = filelist.size();
    channel.Put( &m, sizeof(m) );
    for(list<string>::iterator itr=filelist.begin(); itr!=filelist.end(); itr++)
    {
        channel.Put( (*itr) );
    }

    sqlite3_finalize(stmt);
    sqlite3_close(dbhandle);
    cout <<"EXEC SQL:"<<sql<<endl;
}

//TFSTaskOnTeminateServer
void TFSTaskOnTeminateServer::operator ( )(TFMessage &, TFDuplexChannel &)
{
	std::cout<<"Shuting down TFServer...\nBye!"<<std::endl;
}
